﻿using Microsoft.AspNetCore.Mvc.Filters;
using RuoVea.ExConfig;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using RuoVea.ExUtil;
using RuoVea.ExUtil.Exceptions;
using System.Security.Claims;
using System.Text.Json;
using RuoVea.ExCrypt;
using System.Text;
using System.IdentityModel.Tokens.Jwt;
using System.Web;

namespace RuoVea.Gantt.AuthorizeEx
{
    public class AuthorizeAttribute : Attribute, IAuthorizationFilter
    {
        public int Order { get; set; } = 0;

        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var data = context.HttpContext.User.Identity;
            if (data != null && data.IsAuthenticated == false)
            {
                var cookies = context.HttpContext.Request?.Cookies?.Where(x=>x.Key== "Cookies").Select(x=>x.Value).FirstOrDefault();
                if (cookies.IsNullOrWhiteSpace())
                {
                    var authority = Appsettings.app("Authority:AuthorityUrl")?.ToString();
                    string clientId = Appsettings.app("Authority:ClientId")?.ToString();
                    //string clientSecrets = Appsettings.app("Authority:ClientSecrets")?.ToString();

                    string redirectUris = Appsettings.app("Authority:RedirectUris")?.ToString();
                    string responseType = Appsettings.app("Authority:ResponseType")?.ToString();
                    var hostUrl = Appsettings.app("BaseFile:HostUrl")?.ToString();

                    string enrNonce = AuthorizeExs.EnrNonce();
                    string state = Appsettings.app("Authority:state")?.ToString();
                    string returnUrl = Appsettings.app("BaseFile:HostUrl") + context.HttpContext.Request.Path;
                    returnUrl = HttpUtility.UrlEncode(returnUrl);
                    string uri = $"{authority}/auth/myauthorize?clientid={clientId}&redirecturi={HttpUtility.UrlEncode(hostUrl + redirectUris)}&responsetype={responseType}&state={state}&nonce={enrNonce}&returnurl={returnUrl}";
                    context.HttpContext.Response.Redirect(uri);
                }
            }
        }
    }

    public static class AuthorizeExtion
    {
        public static WebApplication AddEndpoint(this WebApplication app)
        {
            return AuthorizeExs.AddAuthorizeEndpoint(app);
        }
    }

    #region Authority

    /// <summary>
    /// AuthorizeExs
    /// </summary>
    public class AuthorizeExs
    {
        /// <summary>
        /// 添加验证
        /// </summary>
        /// <param name="app"></param>
        /// <returns></returns>
        /// <exception cref="ParamiterException"></exception>
        public static WebApplication AddAuthorizeEndpoint(WebApplication app)
        {
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/sine-olid", async context => { });

                endpoints.MapGet("/callback", async context =>
                 {
                     var returnurl = context?.Request?.Query?.SingleOrDefault(x => x.Key == "returnurl").Value.ToString();

                     var iIdentity = context?.User?.Identity;
                     if (iIdentity != null && !iIdentity.IsAuthenticated)
                     {
                         if (context != null)
                         {
                             var state = context.Request.Query.SingleOrDefault(x => x.Key == "state").Value.ToString();
                             var nonce = context.Request.Query.SingleOrDefault(x => x.Key == "nonce").Value.ToString();

                             var id_token = context.Request.Query.SingleOrDefault(x => x.Key == "id_token").Value.ToString();
                             var token = context.Request.Query.SingleOrDefault(x => x.Key == "token").Value.ToString();
                             if (id_token == "err" || token.IsNullOrWhiteSpace()) { await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("Request invalid")); return; }
                             var access_token = context.Request.Query.SingleOrDefault(x => x.Key == "access_token").Value.ToString();
                             var expires_in = context.Request.Query.SingleOrDefault(x => x.Key == "expires_in").Value.ToString()?.ToDoubleOrNull() ?? 1440;


                         //var token_type = context.Request.Query.SingleOrDefault(x => x.Key == "token_type").Value.ToString();
                         //var scope = context.Request.Query.SingleOrDefault(x => x.Key == "scope").Value.ToString();
                         //var session_state = context.Request.Query.SingleOrDefault(x => x.Key == "session_state").Value.ToString();
                         if (Appsettings.app("Authority:state")?.ToString() != state || state.IsNullOrWhiteSpace() || nonce.IsNullOrWhiteSpace())
                             {
                                 await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("非法请求"));
                                 return;
                             }
                             if (id_token.IsNullOrWhiteSpace())
                             {
                                 await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("id_token invalid"));
                                 return;
                             }
                             if (access_token.IsNullOrWhiteSpace())
                             {
                                 await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("access_token invalid"));
                                 return;
                             }

                             string enrNonce = AuthorizeExs.EnrNonce();
                             if (enrNonce != nonce)
                             {
                                 await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("非法请求"));
                                 return;
                             }
                             var jwtHandler = new JwtSecurityTokenHandler();
                             JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(access_token);

                             var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
                             identity.AddClaims(jwtToken.Claims);
                             ClaimsPrincipal claims = new ClaimsPrincipal(identity) { };
                             await context.SignInAsync(identity.AuthenticationType, claims, new AuthenticationProperties { ExpiresUtc = new System.DateTimeOffset(dateTime: DateTime.Now.AddMinutes(expires_in)) });
                             context.Response.Cookies.Append("Cookies", access_token);
                             if (returnurl.IsNullOrWhiteSpace())
                                 context.Response.Redirect(Appsettings.app("BaseFile:HostUrl")+"/sine-olid");
                             else
                                 context.Response.Redirect(returnurl);
                         }
                     }
                     else
                     {
                         if (returnurl.IsNullOrWhiteSpace())
                             context.Response.Redirect("/sine-olid");
                         else
                             context.Response.Redirect(returnurl);
                     }
                 });
            });
            return app;
        }

        /// <summary>
        /// nonce 加密
        /// </summary>
        /// <returns></returns>
        public static string EnrNonce()
        {
            string clientId = Appsettings.app("Authority:ClientId")?.ToString();
            string clientSecrets = Appsettings.app("Authority:ClientSecrets")?.ToString();
            string redirectUris = Appsettings.app("Authority:RedirectUris")?.ToString();
            string responseType = Appsettings.app("Authority:ResponseType")?.ToString();
            var hostUrl = Appsettings.app("BaseFile:HostUrl")?.ToString();
            string enrValue = clientId + clientSecrets + hostUrl + redirectUris + responseType + DateTime.Now.ToString("yyyyMMddHH");
            string enrNonce = Md5Crypt.Encrypt(enrValue);
            return enrNonce;
        }
    }

    #region Authority
    /// <summary>
    /// 
    /// </summary>
    public class Authority
    {
        /// <summary>
        /// 认证地址
        /// </summary>
        public string AuthorityUrl { get; set; }
        /// <summary>
        /// 客户端Id
        /// </summary>
        public string ClientId { get; set; }
        /// <summary>
        /// 客户端密钥
        /// </summary>
        public string ClientSecrets { get; set; }
        /// <summary>
        /// 回调地址
        /// </summary>
        public string RedirectUris { get; set; }
        /// <summary>
        /// 返回参数
        /// </summary>
        public string ResponseType { get; set; }
        /// <summary>
        /// 私钥 用于自身校验
        /// </summary>
        public string state { get; set; }

        /// <summary>
        /// Authority
        /// </summary>
        public Authority()
        {
            AuthorityUrl = Appsettings.app("Authority:AuthorityUrl")?.ToString();
            ClientId = Appsettings.app("Authority:ClientId")?.ToString();
            ClientSecrets = Appsettings.app("Authority:ClientSecrets")?.ToString();
            RedirectUris = Appsettings.app("Authority:RedirectUris")?.ToString();
            ResponseType = Appsettings.app("Authority:ResponseType")?.ToString();
            state = Appsettings.app("Authority:state")?.ToString();
        }
    }
    #endregion

    #endregion
}
